/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.core.data;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.core.data.extendhandler.ChildEntityAccExtHandler;
import com.inspur.edp.cef.entity.accessor.base.IAccessor;
import com.inspur.edp.cef.entity.accessor.base.ReadonlyDataException;
import com.inspur.edp.cef.entity.accessor.entity.IChildAccessor;
import com.inspur.edp.cef.entity.changeset.AbstractModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.ChangeType;
import com.inspur.edp.cef.entity.changeset.DeleteChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.InnerUtil;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.entity.IMultiLanguageAcc;
import com.inspur.edp.cef.entity.entity.IMultiLanguageData;
import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.DataTypeResInfo;
import com.inspur.edp.cef.spi.extend.datatype.ICefDataExtend;
import com.inspur.edp.cef.spi.extend.datatype.IDataExtendContainer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.var;

public abstract class ChildEntityAccessor extends
    com.inspur.edp.cef.entity.accessor.entity.ChildEntityAccessor
    implements IChildAccessor, IDataExtendContainer, IMultiLanguageAcc {

  protected ChildEntityAccessor(IEntityData data) {
    super(data);
    initExtByInner(data);
    this.initMultiLanguageInfo(data);
  }

  //region
  private ChildEntityAccExtHandler extHandler;

  private ChildEntityAccExtHandler getExtHandler() {
    if (extHandler == null) {
      extHandler = new ChildEntityAccExtHandler(this, getIsReadonly());
    }
    return extHandler;
  }

  @Override
  public void addExtend(ICefDataExtend... ext) {
    Objects.requireNonNull(ext, "ext");

    Arrays.stream(ext).forEach(item -> getExtHandler().addExtend(item));
  }

  @Override
  @JsonIgnore
  public List<ICefDataExtend> getExtends() {
    if (extHandler == null) {
      return Collections.emptyList();
    }
    return extHandler.getExtendList();
  }

  private void initExtByInner(ICefData inner) {
    if (inner != null && inner instanceof IDataExtendContainer) {
      List<ICefDataExtend> exts = ((IDataExtendContainer) inner).getExtends();
      if (exts != null) {
        exts.stream().forEach(ext -> addExtend(ext));
      }
    } else {
      getExtHandler().clear();
    }
  }
  //endregion

  //region
  @Override
  public final Object getValue(String s) {
    RefObject<Object> result = new RefObject<>(null);
    if (getExtHandler().getValue(s, result)) {
      return result.argvalue;
    }
    return innerGetValue(s);
  }

  protected abstract Object innerGetValue(String propName);

  @Override
  public final void setValue(String name, Object value) {
    checkReadonly();
    if (getExtHandler().setValue(name, value)) {
      return;
    }
    innerSetValue(name, value);
  }

  protected abstract void innerSetValue(String name, Object value);

  @Override
  public final ICefData copySelf() {
    return innerCopySelf();
  }

  protected ICefData innerCopySelf() {
    return getInnerData().copySelf();
  }

  @Override
  public final ICefData createChild(String childCode) {
    RefObject<ICefData> result = new RefObject(null);
    if(getExtHandler().createChild(childCode, result)){
      return result.argvalue;
    }
    return innerCreateChild(childCode);
  }

  protected ICefData innerCreateChild(String childCode){
    throw new RuntimeException();
  }

  @Override
  public final HashMap<String, IEntityDataCollection> getChilds() {
    var childs = innerGetChilds();
    var extedChilds = getExtHandler().getChilds();
    if (extedChilds != null) {
      childs.putAll(extedChilds);
    }
    return childs;
  }

  protected abstract HashMap<String, IEntityDataCollection> innerGetChilds();

  @Override
  protected void acceptChangeCore(IChangeDetail change) {
    checkReadonly();
    getExtHandler().acceptChange(change);
    if (change instanceof AbstractModifyChangeDetail) {
      AbstractModifyChangeDetail changeDetail = (AbstractModifyChangeDetail) change;
      InnerUtil.mergeMultiLanguageInfoToChanges(changeDetail.getMultiLanguageInfos(),
          getMultiLanguageInfos(), changeDetail.getPropertyChanges());
      InnerUtil.mergeChangesToMultiLanguageInfo(changeDetail.getPropertyChanges(),
          getMultiLanguageInfos(), null);
    }
    if( getResInfo() != null) {
      if (change.getChangeType() == ChangeType.Modify) {
        AbstractModifyChangeDetail modifyChangeDetail = (AbstractModifyChangeDetail) change;
        for (Map.Entry<String, Object> entry : modifyChangeDetail.getPropertyChanges().entrySet()) {
          if (entry.getValue() instanceof IChangeDetail)
            ((IAccessor) getValue(entry.getKey())).acceptChange((IChangeDetail) entry.getValue());
          else
            setValue(entry.getKey(), entry.getValue());
        }
        acceptModifyChangeExtendInfos((ModifyChangeDetail) modifyChangeDetail);
        return;
      }

      if (change.getChangeType() == ChangeType.Deleted) {
        DeleteChangeDetail deleteChangeDetail = (DeleteChangeDetail) change;
        acceptDeleteChangeExtendInfos(deleteChangeDetail);
        return;
      }
      if (change.getChangeType() == ChangeType.Added) {
        AddChangeDetail addChangeDetail = (AddChangeDetail) change;
        acceptAddChangeExtendInfos(addChangeDetail);
        return;
      }
    }
  }

  protected void acceptModifyChangeExtendInfos(ModifyChangeDetail modifyChangeDetail){
    if(modifyChangeDetail.getChildChanges()==null||modifyChangeDetail.getChildChanges().size()==0)
      return;
    for (Map.Entry<String, Map<String, IChangeDetail>> childChange : modifyChangeDetail
        .getChildChanges().entrySet()) {
      for (Map.Entry<String, IChangeDetail> item : childChange.getValue().entrySet()) {
        IEntityDataCollection childCollection = getChilds().get(childChange.getKey());
        if(childCollection == null){
          throw new IllegalArgumentException("变更集中的子表不存在:"+childChange.getKey());
        }
        if(item.getValue().getChangeType()==ChangeType.Added) {
          childCollection
              .add((IEntityData) ((AddChangeDetail)item.getValue()).getEntityData().copy());
        } else if(item.getValue().getChangeType()==ChangeType.Deleted) {
          childCollection.remove(item.getValue().getDataID());
        }else{
          IEntityData childData = childCollection.getItem(item.getKey());
          if(childData == null){
            throw new IllegalArgumentException(
                "变更集中的子表[" + childChange.getKey() + "]数据不存在:" + item.getKey());
          }
          ((IAccessor) childData).acceptChange(item.getValue());
        }
      }
    }
  }

  private void acceptAddChangeExtendInfos(AddChangeDetail addChangeDetail) {
    if(getParent()!=null) {
      getParent().getChilds().get(getNodeCode()).add(
          (IEntityData) addChangeDetail.getEntityData().copy());
    }
  }

  private void acceptDeleteChangeExtendInfos(DeleteChangeDetail deleteChangeDetail) {
    if(getParent()!=null) {
      getParent().getChilds().get(getNodeCode()).remove(getID());
    }
  }

  protected DataTypeResInfo getResInfo(){
    return null;
  }

  @Override
  protected void onInnerDataChange() {
    super.onInnerDataChange();
    if(getExtHandler().getExtendList().isEmpty()) {
      initExtByInner(getInnerData());
    } else {
      getExtHandler().onInnerDataChanged();
    }
    this.initMultiLanguageInfo(this.getInnerData());
  }

  @Override
  public IEntityDataCollection createAndSetChildCollection(String childCode) {
    var refObj = new RefObject<IEntityDataCollection>(null);
    if(getExtHandler().createAndSetChildCollection(childCode, refObj)){
      return refObj.argvalue;
    }
    return innerCreateAndSetChildCollection(childCode);
  }

  //子类实现
  protected IEntityDataCollection innerCreateAndSetChildCollection(String childCode){
    throw new RuntimeException();
  }

  @Override
  public ICefData copy() {
    ChildEntityAccessor result = (ChildEntityAccessor) super.copy();
    result.extHandler = (ChildEntityAccExtHandler) getExtHandler().copy(result);

    // 多语属性拷贝
    Map<String, MultiLanguageInfo> multiLanguageInfoMap = getMultiLanguageInfos();
    if (multiLanguageInfoMap != null) {
      multiLanguageInfoMap.entrySet().forEach(item -> {
        result.getMultiLanguageInfos().put(item.getKey(), item.getValue());
      });
    }
    return result;
  }

  //endregion


  // region MultiLanguageInfo
  @JsonIgnore
  private Map<String, MultiLanguageInfo> multiLanguageInfos;
  @JsonIgnore
  @Deprecated
  public Map<String, MultiLanguageInfo> getMultiLanguageInfos() {
    if (this.multiLanguageInfos == null) {
      this.multiLanguageInfos = new HashMap<>();
    }
    return multiLanguageInfos;
  }

  private void setMultiLanguageInfos(Map<String, MultiLanguageInfo> infos) {
    this.multiLanguageInfos = infos;
  }

  @Override
  public void setMultiLang(String propName, String langCode, Object value) {
    if(getIsReadonly()) {
      throw new ReadonlyDataException();
    }
    Object orgValue = getMultiLang(propName,langCode);
    raiseMultiLangChanging(propName, langCode, value, orgValue);
    IMultiLanguageAcc.super.setMultiLang(propName, langCode, value);
  }

  private void initMultiLanguageInfo(IEntityData inner) {
    Map<String, MultiLanguageInfo> infos = null;
    if (inner instanceof IMultiLanguageData) {
      IMultiLanguageData cefDataBase = (IMultiLanguageData) inner;
      infos = new HashMap<>(cefDataBase.getMultiLanguageInfos().size(), 1);
      for(Map.Entry<String, MultiLanguageInfo> entrySet: cefDataBase.getMultiLanguageInfos().entrySet()) {
        // 此处克隆，保持回滚能力
        infos.put(entrySet.getKey(),entrySet.getValue().clone());
      }
    }
    this.setMultiLanguageInfos(infos);
  }
  // endregion
}
